package com.king.servlet;

import com.king.annotations.*;
import com.king.pojo.Handler;
import com.king.utils.StringUtil;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName: DispatcherServlet
 * @Description: TODO
 * @Author: wangchangyong
 * @Date: 2020/4/12 10:48
 * @Version: 1.0.0
 **/
public class DispatcherServlet extends HttpServlet {

    // 配置文件
    private Properties properties = new Properties();
    // 扫描包下类的全限定类名
    private List<String> classNames = new ArrayList<>();
    // bean容器
    private Map<String, Object> map = new HashMap<>();
    // HandlerMapping
    private List<Handler> handleMapping = new ArrayList<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 加载配置文件
        String configLocation = config.getInitParameter("configLocation");
        doLoadConfig(configLocation);

        // 扫描相关注解
        doScan(properties.getProperty("scanPackage"));

        // 初始化bean对象（基于注解IOC容器实现）
        doInstance();

        // 依赖注入
        doAutoWired();

        // 构造HandlerMapping处理器映射器，配置URL和Method建立映射关系
        initHandlerMapping();

        System.out.println("ISpring MVC 初始化完成....");
    }

    /**
     * 加载配置文件
     * @param configLocation
     */
    private void doLoadConfig(String configLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(configLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描注解类
     * @param scanPackage
     */
    private void doScan(String scanPackage) {
        String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");
        File pack = new File(scanPackagePath);
        File[] files = pack.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                if (file.isDirectory()) {
                    // 目录，递归扫描
                    doScan(scanPackage + "." + file.getName());
                } else if (file.getName().endsWith(".class")) {
                    String className = scanPackage + "." + file.getName().replaceAll(".class", "");
                    classNames.add(className);
                }
            }
        }
    }

    /**
     * IOC容器
     * 基于全限定类名，反射完成对象创建
     */
    private void doInstance() {
        if (classNames.size() == 0) {
            return;
        }
        try {
            for (String className : classNames) {
                Class<?> aClass = Class.forName(className);
                if (aClass.isAnnotationPresent(IController.class)) {
                    // @IController
                    // IController的beanId首字母小写
                    String beanId = StringUtil.lowerFirst(aClass.getSimpleName());
                    Object object = aClass.newInstance();
                    map.put(beanId, object);
                } else if (aClass.isAnnotationPresent(IService.class)) {
                    // @IService
                    IService annotation = aClass.getAnnotation(IService.class);
                    String beanId = annotation.value();
                    if ("".equals(beanId.trim())) {
                        beanId = StringUtil.lowerFirst(aClass.getSimpleName());
                    }
                    map.put(beanId, aClass.newInstance());

                    // 类实现的接口 以接口名为id放入IOC中 便于根据接口类型注入
                    Class<?>[] interfaces = aClass.getInterfaces();
                    if (interfaces != null && interfaces.length > 0) {
                        for (Class<?> anInterface : interfaces) {
                            // 接口类名为beanId
                            map.put(anInterface.getName(), aClass.newInstance());
                        }
                    }
                } else {
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 依赖注入
     */
    private void doAutoWired() {
        if (map.isEmpty()) {
            return;
        }

        // Bean中是否有@AutoWired注解 维护依赖注入
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
            if (declaredFields != null && declaredFields.length > 0) {
                for (Field declaredField : declaredFields) {
                    if (!declaredField.isAnnotationPresent(IAutowired.class)) {
                        continue;
                    }
                    // 注入依赖
                    IAutowired annotation = declaredField.getAnnotation(IAutowired.class);
                    String beanId = annotation.value();
                    if ("".equals(beanId.trim())) {
                        beanId = declaredField.getType().getName();
                    }
                    // 开启赋值
                    declaredField.setAccessible(true);
                    try {
                        declaredField.set(entry.getValue(), map.get(beanId));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 构造HandlerMapping处理器映射器
     */
    private void initHandlerMapping() {
        if (map.isEmpty()) {
            return;
        }
        // Bean中是否有@RequestMapping注解
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Class<?> aClass = entry.getValue().getClass();
            if (!aClass.isAnnotationPresent(IController.class)) {
                continue;
            }

            String[] securityUser = {};
            // 获取Controller上ISecurity的value
            if (aClass.isAnnotationPresent(ISecurity.class)) {
                ISecurity annotation = aClass.getAnnotation(ISecurity.class);
                securityUser = annotation.value();
            }

            // 获取类上URL
            String baseUrl = null;
            if (aClass.isAnnotationPresent(IRequestMapping.class)) {
                IRequestMapping annotation = aClass.getAnnotation(IRequestMapping.class);
                baseUrl = annotation.value();
            }
            // 获取方法上URL
            String methodUrl;
            Method[] methods = aClass.getMethods();
            if (methods != null && methods.length > 0) {
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(IRequestMapping.class)) {
                        continue;
                    }
                    IRequestMapping annotation = method.getAnnotation(IRequestMapping.class);
                    methodUrl = annotation.value();
                    // 请求URL
                    String url = baseUrl + methodUrl;

                    // 封装Handler
                    Handler handler;

                    // 获取Handler上ISecurity的value
                    if (method.isAnnotationPresent(ISecurity.class)) {
                        ISecurity  securityAnnotation = method.getAnnotation(ISecurity.class);
                        handler = new Handler(entry.getValue(), method, Pattern.compile(url), securityAnnotation.value());
                    }else {
                        handler = new Handler(entry.getValue(), method, Pattern.compile(url), securityUser);
                    }

                    // 方法参数位置信息
                    Parameter[] parameters = method.getParameters();
                    for (int i = 0; i < parameters.length; i++) {
                        Parameter parameter = parameters[i];
                        Class<?> type = parameter.getType();
                        if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                            handler.getParamIndexMapping().put(type.getSimpleName(), i);
                        } else {
                            handler.getParamIndexMapping().put(parameter.getName(), i);
                        }

                    }
                    handleMapping.add(handler);
                }
            }
        }
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        // 获取请求执行的handler
        Handler handler = getHandler(req);
        if (handler == null) {
            resp.getWriter().write("Not found the mapping info of index: " + req.getRequestURI());
            return;
        }

        // 请求参数
        Map<String, String[]> parameterMap = req.getParameterMap();

        // 验证username用户是否有访问权限
        String[] securityUser = handler.getSecurityUser();
        if (securityUser != null && securityUser.length > 0) {
            String username = StringUtils.join(parameterMap.get("username"), ",");
            boolean contains = Arrays.asList(securityUser).contains(username);
            if (!contains) {
                resp.getWriter().write("The mapping no security username: " + username);
                return;
            }
        }

        // 方法参数绑定
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();

        // 方法参数数组
        Object[] args = new Object[parameterTypes.length];

        // handler请求参数
        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
            // 请求参数和方法参数匹配
            if (!handler.getParamIndexMapping().containsKey(param.getKey())) {
                continue;
            }
            // 参数位置
            Integer integer = handler.getParamIndexMapping().get(param.getKey());
            // 参数值
            String value = StringUtils.join(param.getValue(), ",");
            args[integer] = value;
        }

        Integer reqInteger = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
        if(reqInteger != null){
            args[reqInteger] = req;
        }
        Integer respInteger = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
        if(respInteger != null){
            args[respInteger] = resp;
        }

        // 执行方法
        try {
            Object invoke = handler.getMethod().invoke(handler.getController(), args);
            resp.getWriter().write(invoke.toString());
            return;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private Handler getHandler(HttpServletRequest req) {
        if (handleMapping.isEmpty()) {
            return null;
        }
        // 请求URL
        String requestURI = req.getRequestURI();
        for (Handler handler : handleMapping) {
            Matcher matcher = handler.getPattern().matcher(requestURI);
            if (!matcher.matches()) {
                continue;
            }
            return handler;
        }
        return null;
    }


}
